package com.fudan.backend.service.impl;

import com.fudan.backend.dao.*;
import com.fudan.backend.exception.BaseException;
import com.fudan.backend.exception.RetCode;
import com.fudan.backend.pojo.*;
import com.fudan.backend.pojo.statics.*;
import com.fudan.backend.pojo.util.CommodityActivityInfo;
import com.fudan.backend.pojo.util.StoreMoreInfo;
import com.fudan.backend.pojo.vo.CommodityInfo;
import com.fudan.backend.pojo.vo.CommodityInfoList;
import com.fudan.backend.service.AccountService;
import com.fudan.backend.service.CartService;
import com.fudan.backend.service.OrderService;
import com.fudan.backend.util.Snowflake;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class OrderServiceImpl implements OrderService {
    @Autowired
    private OrderDao orderDao;
    @Autowired
    private CommodityDao commodityDao;
    @Autowired
    private ActivityDao activityDao;
    @Autowired
    private StoreDao storeDao;
    @Autowired
    private AccountDao accountDao;
    @Autowired
    private UserDao userDao;
    @Autowired
    private OrderItemDao orderItemDao;
    @Autowired
    private AddressDao addressDao;
    @Autowired
    private CartDao cartDao;
    @Autowired
    private DeliveryInfoDao deliveryInfoDao;
    @Autowired
    private CartService cartService;
    @Autowired
    AccountService accountService;
    @Autowired
    private Snowflake snowflake;
    //订单有效期
    private final long time = 1000L * 60L * 15L;

    @Override
    public Map<String, Object> previewOrder(CommodityInfoList infoList) {
        List<CommodityInfo> list = infoList.getCommodityInfoList();
        Double total = 0.0;
        List<Activity> activityList = new ArrayList<>();
        Map<String, Object> retMap = new HashMap<>();
        if (list.size() == 0) {
            retMap.put("total", total);
            retMap.put("activityList", activityList);
        } else {
            List<String> commodityIdList = list.stream().map(CommodityInfo::getCommodityId).collect(Collectors.toList());
            //获取商品及对应活动，若没参与活动则其中的activity为null
            List<CommodityActivityInfo> cInfoList = commodityDao.selectCommodityInfosByCommodityIdList(commodityIdList);
            //以活动为key，对应未优惠的金额未值
            Map<Activity, Double> acMap = new HashMap<>();
            //未参加活动的商品加入该集合
            Activity defaultActivity = new Activity("-1");
            acMap.put(defaultActivity, 0.0);

            for (CommodityActivityInfo info : cInfoList) {
                Activity activity = info.getActivity();
                Commodity commodity = info.getCommodity();
                //商品未参与活动或活动失效
                if (activity == null || !activity.getCategoryIdList().contains(commodity.getCategoryId()) || activity.getStatus() == ActivityStatus.END
                        || activity.getEndTime().before(new Date(System.currentTimeMillis()))) {
                    Double i = acMap.get(defaultActivity);
                    CommodityInfo commodityInfo = list.stream().filter(c -> c.getCommodityId().equals(commodity.getCommodityId())).findAny().orElse(null);
                    acMap.put(defaultActivity, i + commodityInfo.getCommodityNum() * commodity.getPrice());
                } else {
                    Double i = acMap.get(activity);
                    if (i == null) {
                        acMap.put(activity, 0.0);
                        i = 0.0;
                        activityList.add(activity);
                    }
                    CommodityInfo commodityInfo = list.stream().filter(c -> c.getCommodityId().equals(commodity.getCommodityId())).findAny().orElse(null);
                    acMap.put(activity, i + commodityInfo.getCommodityNum() * commodity.getPrice());
                }
            }
            total = this.calculateTotal(acMap);
            retMap.put("total", total);
            retMap.put("activityList", activityList);
        }
        return retMap;
    }

    @Override
    public List<Order> getAllUserOrder(String userId) {
        return orderDao.selectOrderByUserId(userId);
    }

    @Override
    public PageInfo<Order> getUserOrder(String userId, int status, int current, int size) {
        List<Order> orderList;
        if (status == 6) {
            PageHelper.startPage(current, size);
            orderList = orderDao.selectOrderByUserId(userId);
        } else {
            PageHelper.startPage(current, size);
            orderList = orderDao.selectOrderByUserIdAndStatus(userId, status);
        }
        for (Order order : orderList) {
            this.setOriginSum(order);
        }
        return new PageInfo<>(orderList);
    }

    @Override
    public void payExceptionHandle(BaseException e) {
        int code = e.getCode();
        if (RetCode.ORDER_OVERDUE.getCode().equals(code)) {
            orderDao.setOvertimeOrderStatus(new Date(System.currentTimeMillis() - time));
        } else if (RetCode.ACTIVITY_BALANCE_INSUFFICIENT.getCode().equals(code)) {
            activityDao.cancelInsufficientBalanceActivity();
        }
    }

    @Override
    @Transactional
    public RetCode payOrder(List<String> orderIdList, String userId) throws BaseException {
        List<Order> orderList = orderDao.selectOrdersByOrderIdList(orderIdList);
        //订单与其参与满减商品的原价
        Map<Order, Double> orderOriginMap = new HashMap<>();
        //订单与其获得的满减补贴金额
        Map<Order, Double> orderDiscountMap = new HashMap<>();
        //活动与参与活动商品的总原价，用于后续计算补贴额
        Map<Activity, Double> activityPriceMap = new HashMap<>();
        //用户账户
        Account userAccount = accountDao.selectAccountByAccountId(userDao.selectAccountIdByUserId(userId));
        for (Order order : orderList) {
            if (!order.getUserId().equals(userId)) {
                throw new BaseException(RetCode.UNAUTHORIZED_REQUEST);
            }
            //订单过期
            if (order.getCreateTime().before(new Date(System.currentTimeMillis() - time))) {
                throw new BaseException(RetCode.ORDER_OVERDUE);
            }
            if (!order.getStatus().equals(OrderStatus.NOT_PAID)) {
                throw new BaseException(RetCode.INVALID_REQUEST);
            }
            Double discountPrice = this.calculateDiscountOriginalPrice(order);
            orderOriginMap.merge(order, discountPrice, Double::sum);
        }
        //计算每个活动的总原价
        for (Order order : orderOriginMap.keySet()) {
            Activity activity = activityDao.selectActivityByActivityId(order.getActivityId());
            //活动有效且商店参与活动,则将金额加入
            if (!(activity == null || activity.getStatus() == ActivityStatus.END
                    || activity.getEndTime().before(new Date(System.currentTimeMillis())))) {
                activityPriceMap.merge(activity, orderOriginMap.get(order), Double::sum);
            }
        }
        //验证活动金额是否足够
        for (Activity activity : activityPriceMap.keySet()) {
            Double balance = activity.getBalance();
            double discount = this.calDiscount(activityPriceMap.get(activity), activity);
            if (balance < discount) {
                throw new BaseException(RetCode.ACTIVITY_BALANCE_INSUFFICIENT);
            }
        }
        //计算每个订单获得的补贴
        for (Order order : orderOriginMap.keySet()) {
            Activity activity = activityDao.selectActivityByActivityId(order.getActivityId());
            Order o = new Order(order);
            //活动有效且商店参与活动
            if (!(activity == null || activity.getStatus() == ActivityStatus.END
                    || activity.getEndTime().before(new Date(System.currentTimeMillis())))) {
                //该商店补贴商品原价
                double origin = orderOriginMap.get(order);
                //对应活动的总价
                double totalPrice = activityPriceMap.get(activity);
                //本次支付改商店补贴
                double allowance = this.calDiscount(totalPrice, activity) * (origin / totalPrice);

                o.setAllowance(allowance);
                orderDiscountMap.put(o, allowance);
            } else {
                o.setAllowance(0.0);
                orderDiscountMap.put(o, 0.0);
            }
        }
        Date current = new Date(System.currentTimeMillis());
        //活动余额信息数据库更新
        for (Activity activity : activityPriceMap.keySet()) {
            Double balance = activity.getBalance();
            double discount = this.calDiscount(activityPriceMap.get(activity), activity);
            if (balance < discount) {
                throw new BaseException(RetCode.ACTIVITY_BALANCE_INSUFFICIENT);
            }
            activityDao.updateActivityBalance(activity.getActivityId(), balance - discount);
        }
        //转账与order表更新,生成发货信息
        for (Order order : orderDiscountMap.keySet()) {
            //订单总原价
            double totalOrigin = this.calculateOrderOriginalPrice(order);
            double payment = totalOrigin - order.getAllowance();
            Order o = new Order(order);

            String billId = accountService.transfer(userAccount.getAccountId(), AccountStatic.INTERMEDIATE_ACCOUNT_ID, payment, BillType.PAY_ORDER, "支付订单");
            o.setPayment(payment);
            o.setPayTime(current);
            o.setStatus(OrderStatus.PAID_AND_NOT_SHIPPED);
            o.setBillId(billId);
            orderDao.updateOrder(o);
            DeliveryInfo deliveryInfo = new DeliveryInfo(snowflake.nextId().toString(), order.getStoreId(), order.getOrderId(), order.getAddressId(), DeliveryStatus.NOT_SHIPPED);
            deliveryInfoDao.addDeliveryInfo(deliveryInfo);
        }
        return RetCode.ORDER_SUCCESS;
    }

    @Override
    @Transactional
    public Order confirmOrderFromCommodity(String commodityId, String addressId, int commodityNum, String moreInfo, String userId) {
        Commodity commodity = commodityDao.selectCommodityByCommodityId(commodityId);
        if (commodity == null) {
            throw new BaseException(RetCode.COMMODITY_NOT_FOUND);
        }
        if (commodity.getStocks() < commodityNum) {
            throw new BaseException(RetCode.COMMODITY_STOCKS_INSUFFICIENT);
        }
        Address address = addressDao.getAddressByAddressId(addressId);
        if (address == null) {
            throw new BaseException(RetCode.ADDRESS_NOT_FOUND);
        }
        if (!address.getUserId().equals(userId)) {
            throw new BaseException(RetCode.UNAUTHORIZED_REQUEST);
        }
        Order order = this.generateSingleCommodityOrder(commodityId, commodityNum, userId, addressId, moreInfo);
        OrderItem orderItem = order.getOrderItemList().get(0);
        orderDao.insertOrder(order);
        orderItemDao.insertOrderItem(orderItem);
        this.setOriginSum(order);
        return order;
    }

    @Override
    @Transactional
    public List<Order> confirmOrderFromCart(List<String> cartItemIdList, String addressId, List<StoreMoreInfo> moreInfoList, String userId) {
        Address address = addressDao.getAddressByAddressId(addressId);
        if (address == null) {
            throw new BaseException(RetCode.ADDRESS_NOT_FOUND);
        }
        if (!address.getUserId().equals(userId)) {
            throw new BaseException(RetCode.UNAUTHORIZED_REQUEST);
        }
        Map<String, List<CartItem>> cartMap = cartService.getCartItemsMapByCartId(cartItemIdList);
        List<Order> orderList = this.generateOrderFromCartMap(cartMap, userId, addressId, moreInfoList);
        for (Order order : orderList) {
            orderDao.insertOrder(order);
            orderItemDao.insertOrderItemList(order.getOrderItemList());
        }
        cartDao.deleteCartItemsByCartItemIdList(cartItemIdList);
        for (Order order : orderList) {
            this.setOriginSum(order);
        }
        return orderList;
    }

    @Override
    public RetCode quashOrder(String orderId, String userId) {
        Order order = orderDao.selectOrderByOrderId(orderId);
        if (order == null) {
            throw new BaseException(RetCode.ORDER_NOT_FOUND);
        }
        if (!order.getUserId().equals(userId)) {
            throw new BaseException(RetCode.UNAUTHORIZED_REQUEST);
        }
        if (!order.getStatus().equals(OrderStatus.NOT_PAID)) {
            throw new BaseException(RetCode.ORDER_CANT_QUASH);
        }
        orderDao.setOrderStatus(orderId, OrderStatus.CANCELLED);
        return RetCode.ORDER_SUCCESS;
    }

    @Override
    public Double storeSalesValue(String storeId, Date startTime, Date endTime) {
        List<Order> orders = orderDao.selectOrderByStoreIdAndTimeAndStatus(storeId, startTime, endTime, OrderStatus.RECEIVED);
        double value = 0.0;
        for (Order order : orders) {
            value += this.calculateOrderOriginalPrice(order);
        }
        return value;
    }

    @Override
    public int storeSalesVolume(String storeId, Date startTime, Date endTime) {
        List<Order> orders = orderDao.selectOrderByStoreIdAndTimeAndStatus(storeId, startTime, endTime, OrderStatus.RECEIVED);
        int volume = 0;
        for (Order order : orders) {
            for (OrderItem item : order.getOrderItemList()) {
                volume += item.getCommodityNum();
            }
        }
        return volume;
    }

    @Override
    @Transactional
    public RetCode deleteOrder(List<String> orderIdList, String userId) {
        for (Order order : orderDao.selectOrdersByOrderIdList(orderIdList)) {
            int status = order.getStatus();
            if (!order.getUserId().equals(userId)) {
                throw new BaseException(RetCode.UNAUTHORIZED_REQUEST);
            }
            if (status == OrderStatus.RECEIVED
                    || status == OrderStatus.CANCELLED
                    || status == OrderStatus.REFUND) {
                orderDao.deleteOrderByOrderId(order.getOrderId());
            } else {
                throw new BaseException(RetCode.ORDER_CANT_DELETE);
            }
        }
        return RetCode.ORDER_SUCCESS;
    }

    @Override
    @Transactional
    public RetCode receiveOrder(String orderId, String userId) {
        Order order = orderDao.selectOrderByOrderId(orderId);
        if (order == null) {
            throw new BaseException(RetCode.ORDER_NOT_FOUND);
        }
        Store store = storeDao.selectStoreByStoreId(order.getStoreId());
        Date current = new Date(System.currentTimeMillis());

        if (!order.getUserId().equals(userId)) {
            throw new BaseException(RetCode.UNAUTHORIZED_REQUEST);
        }
        //销量更新
        for (OrderItem item : order.getOrderItemList()) {
            commodityDao.addCommoditySaleVolume(item.getCommodityId(), item.getCommodityNum());
        }

        order.setTakeTime(current);
        order.setStatus(OrderStatus.RECEIVED);
        double total = order.getAllowance() + order.getPayment();
        double commission = order.getPayment() * 0.05;
        orderDao.updateOrder(order);
        accountService.transfer(AccountStatic.INTERMEDIATE_ACCOUNT_ID, store.getAccountId(), total - commission, BillType.COMMODITY_ARRIVE, "商品到货转入商店账户");
        accountService.transfer(AccountStatic.INTERMEDIATE_ACCOUNT_ID, AccountStatic.WEBSHOP_ACCOUNT_ID, commission, BillType.COMMISSION, "佣金");
        return RetCode.ORDER_SUCCESS;
    }

    /**
     * 计算总价
     */
    private Double calculateTotal(Map<Activity, Double> map) {
        Double total = 0.0;
        for (Activity activity : map.keySet()) {
            Double i = map.get(activity);
            if (activity.getActivityId().equals("-1")) {
                total += i;
            } else {
                Double discount = this.calDiscount(i, activity);
                total += (i - discount);
            }
        }
        return total;
    }

    /**
     * 计算满减额
     *
     * @param activity 活动
     * @param value    金额
     * @return 满减额
     */
    private double calDiscount(double value, Activity activity) {
        return Math.floor(value / activity.getReach()) * activity.getReduce();
    }

    /**
     * 生成orderItem项
     */
    private OrderItem generateOrderItem(String commodityId, int num, String orderId) {
        Commodity commodity = commodityDao.selectCommodityByCommodityId(commodityId);
        OrderItem orderItem = new OrderItem();
        if (commodity == null) {
            throw new BaseException(RetCode.COMMODITY_NOT_FOUND);
        }
        orderItem.setOrderItemId(snowflake.nextId().toString());
        orderItem.setCommodity(commodity);
        orderItem.setCommodityId(commodityId);
        orderItem.setCommodityNum(num);
        orderItem.setOrderId(orderId);
        Store store = storeDao.selectStoreByStoreId(commodity.getStoreId());
        Activity activity = activityDao.selectActivityByActivityId(store.getActivityId());
        //商品未参与活动或活动失效
        if (activity == null || !activity.getCategoryIdList().contains(commodity.getCategoryId()) || activity.getStatus() == ActivityStatus.END
                || activity.getEndTime().before(new Date(System.currentTimeMillis()))) {
            orderItem.setIsAllowanced(OrderItemStatic.NOT_DISCOUNT);
        } else {
            orderItem.setIsAllowanced(OrderItemStatic.IS_DISCOUNT);
        }
        return orderItem;
    }

    /**
     * 数据库批量增添orderItem
     */
    private List<OrderItem> addOrderItem(List<String> commodityIdList, int num, String orderId) {
        List<OrderItem> itemList = new ArrayList<>();
        for (String commodityId : commodityIdList) {
            itemList.add(this.generateOrderItem(commodityId, num, orderId));
        }
        orderItemDao.insertOrderItemList(itemList);
        return itemList;
    }

    /**
     * 单商品订单的生成
     */
    private Order generateSingleCommodityOrder(String commodityId, int num, String userId, String addressId, String moreInfo) {
        String orderId = snowflake.nextId().toString();
        OrderItem orderItem = this.generateOrderItem(commodityId, num, orderId);
        Commodity commodity = orderItem.getCommodity();
        Store store = storeDao.selectStoreByStoreId(commodity.getStoreId());
        Activity activity = activityDao.selectActivityByActivityId(store.getActivityId());
        String activityId;
        if (activity == null) {
            activityId = "0";
        } else {
            activityId = "0";
            if (activity.getCategoryIdList().contains(commodity.getCategoryId())) {
                activityId = store.getActivityId();
            }
        }

        List<OrderItem> itemList = Collections.singletonList(orderItem);
        return new Order(orderId, userId, commodity.getStoreId(),
                new Date(System.currentTimeMillis()), null, null, null, null, addressId,
                activityId, null, null, OrderStatus.NOT_PAID, 0, moreInfo, itemList);
    }

    /**
     * 由按storeId分组后的购物车map生成订单
     */
    private List<Order> generateOrderFromCartMap(Map<String, List<CartItem>> cartMap, String userId, String addressId, List<StoreMoreInfo> infoList) {
        List<Order> orderList = new ArrayList<>();
        String activityId = "0";
        for (String storeId : cartMap.keySet()) {
            Store store = storeDao.selectStoreByStoreId(storeId);
            if (store == null) {
                throw new BaseException(RetCode.STORE_NOT_FOUND);
            }
            List<OrderItem> itemList = new ArrayList<>();
            String orderId = snowflake.nextId().toString();
            String moreInfo = infoList.stream().filter(info -> info.getStoreId().equals(storeId)).findFirst().orElse(new StoreMoreInfo(storeId, "")).getMoreInfo();
            for (CartItem cartItem : cartMap.get(storeId)) {
                OrderItem orderItem = this.generateOrderItem(cartItem.getCommodityId(), cartItem.getCommodityNum(), orderId);
                itemList.add(orderItem);
                if (orderItem.getIsAllowanced().equals(1)) {
                    activityId = store.getActivityId();
                }
            }
            orderList.add(new Order(orderId, userId, storeId, new Date(System.currentTimeMillis()), null, null, null, null, addressId,
                    activityId, null, null, OrderStatus.NOT_PAID, 0, moreInfo, itemList));
        }
        return orderList;
    }

    /**
     * 计算订单原价
     */
    private Double calculateOrderOriginalPrice(String orderId) {
        Order order = orderDao.selectOrderByOrderId(orderId);
        double total = 0.0;
        for (OrderItem item : order.getOrderItemList()) {
            total += (item.getCommodity().getPrice() * item.getCommodityNum());
        }
        return total;
    }

    /**
     * 计算订单原价
     */
    private Double calculateOrderOriginalPrice(Order order) {
        double total = 0.0;
        for (OrderItem item : order.getOrderItemList()) {
            total += (item.getCommodity().getPrice() * item.getCommodityNum());
        }
        return total;
    }

    /**
     * 计算打折商品原价
     */
    private Double calculateDiscountOriginalPrice(String orderId) {
        Order order = orderDao.selectOrderByOrderId(orderId);
        double total = 0.0;
        for (OrderItem item : order.getOrderItemList()) {
            if (item.getIsAllowanced().equals(OrderItemStatic.IS_DISCOUNT)) {
                total += (item.getCommodity().getPrice() * item.getCommodityNum());
            }
        }
        return total;
    }

    /**
     * 计算打折商品原价
     */
    private Double calculateDiscountOriginalPrice(Order order) {
        double total = 0.0;
        for (OrderItem item : order.getOrderItemList()) {
            if (item.getIsAllowanced().equals(OrderItemStatic.IS_DISCOUNT)) {
                total += (item.getCommodity().getPrice() * item.getCommodityNum());
            }
        }
        return total;
    }

    private void setOriginSum(Order order) {
        double v = this.calculateOrderOriginalPrice(order);
        order.setOriginSum(v);
    }
}
